import * as execa from 'execa';
import * as vscode from 'vscode';
import { parse } from 'url';

export class Git {
  private async execute(
    cmd: string,
    uri: vscode.Uri
  ): Promise<{ stdout: string; stderr: string }> {
    const [git, ...args] = cmd.split(' ');

    return execa(git, args, { cwd: uri.fsPath });
  }

  private async getRemoteNames(uri: vscode.Uri): Promise<string[]> {
    const remotes = (await this.execute(
      `git config --local --get-regexp ^remote.*.url`,
      uri
    )).stdout.trim();

    return remotes
      .split('\n')
      .map(line => new RegExp('^remote.([^.]+).url.*').exec(line))
      .map(match => match && match[1])
      .filter(name => Boolean(name)) as string[];
  }

  private async getRemoteUrl(remoteName: string, uri: vscode.Uri): Promise<string> {
    try {
      const remote = (await this.execute(
        `git config --local --get remote.${remoteName}.url`,
        uri
      )).stdout.trim();
      if (!remote.length) {
        throw new Error('Git remote is empty!');
      }
      return remote;
    } catch (e) { }
    return '';
  }

  /** 打开仓库在线页面 */
  public async openRepositoryUrl(uri: vscode.Uri) {
    const remoteNames = await this.getRemoteNames(uri);
    if (!remoteNames || !remoteNames.length) {
      vscode.window.showInformationMessage('没有找到远程仓库链接。');
    }
    else {
      const remoteUrls = await Promise.all(remoteNames.map(async (remoteName) => {
        return this.getRemoteUrl(remoteName, uri);
      })).then((remoteUrls: string[]) => {
        return remoteUrls.map(item => ({
          label: item,
        }));
      });
      vscode.window.showQuickPick(
        remoteUrls,
        {
          placeHolder: '选择要打开的仓库链接',
        }
      ).then(selected => {
        if (selected) {
          const repoUrl = this.remoteUri2RepoUrl(selected.label);
          vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(repoUrl));
        }
      });
    }
    return null;
  }

  private remoteUri2RepoUrl(remote: string): string {
    const [, host, user, repo] = this.parseGitUrl(remote);
    return `https://${host}/${user}/${repo}`;
  }

  public parseGitUrl(remote: string): string[] {
    // git protocol remotes, may be git@github:username/repo.git
    // or git://github/user/repo.git, domain names are not case-sensetive
    if (remote.startsWith('git@') || remote.startsWith('git://')) {
      return this.parseGitProviderUrl(remote);
    }

    return this.getGitProviderOwnerAndRepositoryFromHttpUrl(remote);
  }

  public parseGitProviderUrl(remote: string): string[] {
    const match = new RegExp(
      '^git(?:@|://)([^:/]+)(?::|:/|/)([^/]+)/(.+?)(?:.git)?$',
      'i'
    ).exec(remote);
    if (!match) {
      throw new Error(
        `'${remote}' does not seem to be a valid git provider url.`
      );
    }
    return ['git:', ...match.slice(1, 4)];
  }

  private getGitProviderOwnerAndRepositoryFromHttpUrl(
    remote: string
  ): string[] {
    // it must be http or https based remote
    const { protocol, hostname, pathname } = parse(remote);
    // domain names are not case-sensetive
    if (!hostname || !pathname) {
      throw new Error('Not a Provider remote!');
    }
    const match = pathname.match(/\/(.*?)\/(.*?)(?:.git)?$/);
    if (!match) {
      throw new Error('Not a Provider remote!');
    }

    return [protocol || 'https:', hostname, ...match.slice(1, 3)];
  }

}